@file:Suppress("UNCHECKED_CAST")

package io.modifier.basic.utils

import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.jvm.JvmOverloads
import kotlin.reflect.KClass

object FEvent {
    private val _map = mutableMapOf<KClass<*>, MutableSharedFlow<*>>()

    suspend fun <T : Any> emit(
        event: T,
        key: KClass<T> = event::class as KClass<T>,
    ) {
        withContext(Dispatchers.Main) {
            emitInternal(event, key)
        }
    }

    internal suspend fun <T : Any> emitInternal(
        event: T,
        key: KClass<T>,
    ) {
        @Suppress("UNCHECKED_CAST")
        val flow = _map[key] as? MutableSharedFlow<T>
        flow?.emit(event)
    }

    suspend inline fun <reified T : Any> collect(noinline block: suspend (T) -> Unit) = collect(T::class, block)

    suspend fun <T : Any> collect(
        key: KClass<T>,
        block: suspend (T) -> Unit,
    ) {
        withContext(Dispatchers.Main) {
            @Suppress("UNCHECKED_CAST")
            val flow = _map.getOrPut(key) { MutableSharedFlow<T>() } as MutableSharedFlow<T>
            try {
                flow.collect { block(it) }
            } finally {
                if (flow.subscriptionCount.value == 0) {
                    _map.remove(key)
                }
            }
        }
    }
}

@JvmOverloads
fun <T : Any> FEvent.post(
    event: T,
    key: KClass<T> = event::class as KClass<T>,
) {
    @OptIn(DelicateCoroutinesApi::class)
    GlobalScope.launch(Dispatchers.Main) {
        emitInternal(event, key)
    }
}

inline fun <reified T : Any> FEvent.flowOf(): Flow<T> = flowOf(T::class)

fun <T : Any> FEvent.flowOf(key: KClass<T>): Flow<T> = channelFlow {
    collect(key) { send(it) }
}